feat(center-stage): multi-person group framing + shot-size headroom + lead-room#118
Merged
Conversation
… lead-room
Digital framer path only (no physical-PTZ controller changes).
D1 — Multi-person group framing (union bbox):
- new tracking.group_framing toggle (default False = no behaviour change)
- union_bbox() pure helper + CameraWorker._group_union_bbox() over the live
confident, non-lost TrackInfo list
- desired_crop now sizes the crop to fit the subject both vertically and
horizontally, so a WIDE union auto-widens (zoom out) to keep everyone in
shot — still aspect-locked and capped by max_frac. Tall single-person boxes
stay height-driven exactly as before.
- Explicit lock wins: a locked target (by track id or configured identity)
always keeps following that one person, even with group mode on; group
framing applies only when no explicit target is locked. trusted_bbox
fallback for single-target churn is preserved.
D2 — Shot-size-aware headroom:
- _CENTERSTAGE_FRAMING carries a per-preset headroom (face 0.06 →
head_shoulders 0.08 → upper_body 0.10 → full_body 0.14); _framed_output
sets DigitalFramer.headroom from the active preset alongside fill/max_frac.
D2 — Subtle digital lead-room ("nose room"):
- new tracking.lead_room gain (default 0.0 = off/centred, exactly as before).
DigitalFramer tracks an EMA of the subject-centre velocity and offsets the
crop centre toward the motion, capped to 12% of the crop so it can't
destabilise framing.
All existing digital_framer tests (dead-zone hold, aspect lock, split size
smoothing) preserved. Gates: ruff/format clean, mypy strict clean, 1325
pytest passed, selftest passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…oup union Review of PR #118 found two minors: - Lock-wins leak: an explicit lock with no live track and no trusted_bbox fell through to the group union. Now an explicit lock returns None instead — it always wins, even transiently. - The width-aware crop sizing now requires fit_width=True (passed only for a multi-person union via _digital_target_is_group); single-person / non-group framing stays strictly height-only, byte-identical to prior behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Adds the marquee Center-Stage feature AutoPTZ lacked — multi-person framing — plus two composition-polish knobs, all in the digital framer path (no physical-PTZ controller changes). Every default reproduces prior behaviour exactly.
D1 — Multi-person group framing (union bbox)
tracking.group_framing: bool = Falsetoggle (off → no behaviour change).union_bbox()pure helper indigital_framer.py+CameraWorker._group_union_bbox()over the live confident, non-lostTrackInfoboxes.desired_cropnow sizes the crop to fit the subject both vertically and horizontally, so a WIDE union auto-widens (zoom out) to keep everyone in shot — still aspect-locked and capped bymax_frac. Tall single-person boxes stay height-driven exactly as before.trusted_bboxfallback for single-target churn is preserved.D2 — Shot-size-aware headroom
_CENTERSTAGE_FRAMINGcarries a per-preset headroom:face 0.06 → head_shoulders 0.08 → upper_body 0.10 (unchanged midpoint) → full_body 0.14. Closer shots get less margin above the head, full-body more._framed_outputsetsDigitalFramer.headroomfrom the active preset, the same wayfill/max_fracare already wired (live, no restart).D2 — Subtle digital lead-room ("nose room")
tracking.lead_room: float = 0.0gain (default off / centred,lead=0reproduces prior framing).DigitalFramertracks an EMA of the framed subject's centre velocity and offsets the crop centre toward the motion, capped to 12% of the crop so it can't destabilise framing.Tests
union_bbox/_group_union_bbox(pure) tested directly; group crop frames the UNION and is wider than any single box; lost people ignored.lead=0→ centred as before; default stays subtle.digital_framertests (dead-zone hold, aspect lock, split size smoothing) preserved.Gates
ruff check/ruff format --check: cleanmypy autoptz/engine/runtime/ autoptz/config/(strict): cleanpytest tests/: 1325 passedpython -m autoptz --selftest: all checks passed🤖 Generated with Claude Code